/*
 * MIT License
 *
 * Copyright (c) 2019 Mr.css
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package cn.seaboot.commons.file;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Hashtable;
import java.util.Map;

/**
 * 二维码，BufferedImage可以通过ImageIO进一步处理
 *
 * @author Mr.css 2017-08-14 14:38:42
 */
public class QRCodeUtil {
    private QRCodeUtil() {
    }

    private static final int BLACK = 0xFF000000;
    private static final int WHITE = 0xFFFFFFFF;

    /**
     * 创建一个二维码
     *
     * @param text   文本信息
     * @param width  图片宽度
     * @param height 图片高度
     * @return BufferedImage
     * @throws WriterException -
     */
    public static BufferedImage createQRCode(String text, int width, int height)
            throws WriterException {
        return createCode(BarcodeFormat.QR_CODE, text, Charset.defaultCharset(), width, height, ErrorCorrectionLevel.L);
    }

    /**
     * 使用ImageIO写入文件
     *
     * @param text    文本
     * @param width   宽度
     * @param height  高度
     * @param charSet 编码
     * @param format  图片类型，二维码是QR_CODE
     * @param level   容错率，与图片大小相关，容错率越高，图片越大
     */
    public static BufferedImage createCode(BarcodeFormat format, String text, Charset charSet, int width, int height, ErrorCorrectionLevel level)
            throws WriterException {
        Map<EncodeHintType, Object> hints = new Hashtable<>();
        hints.put(EncodeHintType.CHARACTER_SET, charSet);
        hints.put(EncodeHintType.ERROR_CORRECTION, level);
        BitMatrix bitMatrix = new MultiFormatWriter().encode(text, format, width, height, hints);
        return toBufferedImage(bitMatrix);
    }

    /**
     * BitMatrix 转 BufferedImage
     *
     * @param matrix 图形矩阵
     * @return Image
     */
    public static BufferedImage toBufferedImage(BitMatrix matrix) {
        int width = matrix.getWidth();
        int height = matrix.getHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
            }
        }
        return image;
    }

    /**
     * 简单解码，默认的最简单解码
     *
     * @param input 输入流
     */
    public static String decode(InputStream input) throws IOException, NotFoundException {
        BufferedImage image = ImageIO.read(input);
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        Map<DecodeHintType, Object> hints = new Hashtable<>();
        hints.put(DecodeHintType.CHARACTER_SET, Charset.defaultCharset());
        Result result = new MultiFormatReader().decode(bitmap, hints);
        return result.getText();
    }

    /**
     * 高精度解码，通过调整API的一些参数，提高解码准确率
     *
     * @param input 输入流
     */
    public static String decodeHighPrecision(InputStream input) throws NotFoundException, IOException {
        BufferedImage image = ImageIO.read(input);
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        Map<DecodeHintType, Object> hints = new Hashtable<>();
        hints.put(DecodeHintType.CHARACTER_SET, Charset.defaultCharset());
        // 优化精度，花更多的时间用于寻找图上的编码，优化准确性，但不优化速度，Boolean类型
        hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
        // 	Boolean类型，指定图片是一个纯粹的二维码
        hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
        Result result = new MultiFormatReader().decode(bitmap, hints);
        return result.getText();
    }
}
